package cn.cosmosx.base.util.check;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 组织机构代码校验
 *
 * @author gengzhy
 * @date 2022-9-22
 */
public final class OrgCodeCheckUtil {

    /**
     * 组织机构代码原字符（二代）
     */
    private final static String[] ORIGIN_CODE = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                                                  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                                                  "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
                                                  "U", "V", "W", "X", "Y", "Z" };

    /**
     * 组织机构代码原字符对应的机器码（二代）
     */
    private final static int[] MACHINE_CODE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                                                10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
                                                20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                                                30, 31, 32, 33, 34, 35 };
    /**
     * 八位本地吗位置对应的加权因子（二代）
     */
    private final static int[] ORIGIN_POS_FACTOR = { 3 , 7 , 9 , 10, 5 , 8 , 4 , 2 };

    /**
     * 组织机构代码正则校验
     */
    private final static Pattern REGEX_ORG_CODE = Pattern.compile("^[0-9A-Z]{8}-[0-9A-Z]$");

    /**
     * 组织机构代码原字符与其对应的机器码数据集
     */
    private final static Map<String, Integer> REFER = new HashMap<>();
    static {
        for (int i = 0, len = ORIGIN_CODE.length; i < len; i++) {
            REFER.put(ORIGIN_CODE[i], MACHINE_CODE[i]);
        }
    }

    /**
     * 组织机构代码证校验
     * <pre>
     *     标准：GB 11714-1997
     *     组织机构代码证的格式为????????-?，其中?为数字或者字母（八位本体代码加一位校验码构成）
     * </pre>
     *
     * @param orgCode 组织机构代码
     * @return boolean True-合法；False-不合法
     */
    public static boolean verify(String orgCode) {
        return verify_2Ed(orgCode) || verify_1Ed(orgCode);
    }

    /**
     * 组织机构代码证校验（二代）
     * <pre>
     *     标准：GB 11714-1997
     *     组织机构代码证的格式为????????-?，其中?为数字或者字母（八位本体代码加一位校验码构成）
     * </pre>
     *
     * @param orgCode 组织机构代码
     * @return boolean True-合法；False-不合法
     */
    public static boolean verify_2Ed(String orgCode) {
        if (!REGEX_ORG_CODE.matcher(orgCode).matches()) {
            return false;
        }
        // 1、取组织机构代码的八位本体码为基数，取对应的加权因子
        String[] codes = orgCode.split("-");
        // 本体码
        String originCode = codes[0];
        // 校验码
        String checkCode = codes[1];

        // 2、本体码对应的机器码与对应的加权因子相乘求和
        String[] originCodeArr = originCode.split("");
        int sum = 0;
        for (int i = 0; i < originCodeArr.length; i++) {
            Integer machineCode = REFER.get(originCodeArr[i]);
            if (machineCode == null) {
                return false;
            }
            sum += machineCode * ORIGIN_POS_FACTOR[i];
        }

        // 3、求得的和除以11得到余数，用11减去余数得到的结果（机器码），与校验码对应的机器码做比对
        int result = 11 - (sum % 11);
        // 注意：当计算得到的校验码数值result为零时，原校验码用“0”表示，而不是用“B”
        if (result == 11 && REFER.get(checkCode) == 0) {
            return true;
        }
        return REFER.get(checkCode) == result;
    }

    /**
     * 组织机构代码证校验（一代）
     * <pre>
     *     标准：GB 11714-1995
     *     组织机构代码证的格式为????????-?，其中?为数字或者字母
     * </pre>
     *
     * @param orgCode 组织机构代码
     * @return boolean True-合法；False-不合法
     */
    public static boolean verify_1Ed(String orgCode) {
        if (!REGEX_ORG_CODE.matcher(orgCode).matches() || "00000000-0".equals(orgCode)) {
            return false;
        }
        try {
            byte[] codes = orgCode.getBytes("iso-8859-1");
            int w_i[] = { 3, 7, 9, 10, 5, 8, 4, 7 };
            int c_i[] = new int[8];
            int s = 0;
            if (codes[8] != 45) {
                return false;
            }
            int c;
            for (int i = 0; i < 10; i++) {
                c = codes[i];
                if (c <= 122 && c >= 97) {
                    return false;
                }
            }
            byte fir_value = codes[0];
            byte sec_value = codes[1];
            if (fir_value >= 65 && fir_value <= 90) {
                c_i[0] = (fir_value + 32) - 87;
            } else if (fir_value >= 48 && fir_value <= 57) {
                c_i[0] = fir_value - 48;
            } else {
                return false;
            }
            s += w_i[0] * c_i[0];
            if (sec_value >= 65 && sec_value <= 90) {
                c_i[1] = (sec_value - 65) + 10;
            } else if (sec_value >= 48 && sec_value <= 57) {
                c_i[1] = sec_value - 48;
            } else {
                return false;
            }
            s += w_i[1] * c_i[1];
            for (int j = 2; j < 8; j++) {
                if (codes[j] < 48 || codes[j] > 57) {
                    return false;
                }
                c_i[j] = codes[j] - 48;
                s += w_i[j] * c_i[j];
            }
            c = 11 - s % 11;
            return codes[9] == 88 && c == 10  || c == 11 && codes[9] == 48 || c == codes[9] - 48;
        } catch (UnsupportedEncodingException e) {
            // nothing to do
        }
        return false;
    }

}
